package com.pf.utils;

import com.github.rholder.retry.*;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/**
 * guava 重试
 *
 * @author PanFei
 * @version 1.0.0
 * @createTime 2021/8/12
 */
@Slf4j
public class GuavaRetryingUtils {

    public static void main(String[] args) {
        test1();
        // test2();
    }

    /**
     * 测试1：目标方法有返回值
     */
    private static void test1() {
        String result = GuavaRetryingUtils.retry(3, 1, new MyRetryListener(), () -> {
            // 模拟调用接口
            return invokeMethod();
        }, Exception.class, String.class);

        log.info(">>>> 最终的返回结果：{}", result);
    }

    /**
     * 测试方法2:目标方法无返回值
     */
    private static void test2() {
        GuavaRetryingUtils.retry(3, 1, new MyRetryListener(), () -> {
            say();
            return null;
        }, Exception.class);
    }

    /**
     * 重试 ：适用于目标重试方法无返回值
     *
     * @param attemptNumber  重试次数
     * @param sleepTime      重试间隔时间
     * @param retryListener  重试监听器
     * @param callable       重试的目标方法
     * @param exceptionClazz 异常类型
     */
    public static void retry(int attemptNumber, long sleepTime, RetryListener retryListener, Callable callable, Class<? extends Throwable> exceptionClazz) {
        Retryer<Object> retryer = RetryerBuilder.newBuilder()
                // 目标函数返回空时，触发重试策略
                //.retryIfResult(Predicates.isNull())
                // 目标函数返回Exception异常时，触发重试策略
                .retryIfExceptionOfType(exceptionClazz)
                // 重试次数
                .withStopStrategy(StopStrategies.stopAfterAttempt(attemptNumber))
                // 重试间隔时间 重试时间每次递增2秒
                .withWaitStrategy(WaitStrategies.incrementingWait(sleepTime, TimeUnit.SECONDS, 2, TimeUnit.SECONDS))
                // 重试监听器
                .withRetryListener(retryListener)
                .build();
        try {
            retryer.call(callable);
        } catch (Exception e) {
            log.error(">>>>> GuavaRetryingUtil retry error!", e);
        }

    }


    /**
     * 重试：适用于目标重试方法有返回值
     *
     * @param attemptNumber  重试次数
     * @param sleepTime      重试间隔时间
     * @param retryListener  重试监听器
     * @param callable       重试的目标方法
     * @param exceptionClazz 异常类型
     * @param resultClazz    重试目标方法返回类型
     * @param <T>
     * @return
     */
    public static <T> T retry(int attemptNumber, long sleepTime, RetryListener retryListener, Callable callable, Class<? extends Throwable> exceptionClazz, Class<T> resultClazz) {
        Retryer<Object> retryer = RetryerBuilder.newBuilder()
                // 目标函数返回空时，触发重试策略
                //.retryIfResult(Predicates.isNull())
                // 目标函数返回Exception异常时，触发重试策略
                .retryIfExceptionOfType(exceptionClazz)
                // 重试次数
                .withStopStrategy(StopStrategies.stopAfterAttempt(attemptNumber))
                // 重试间隔时间 重试时间每次递增2秒
                .withWaitStrategy(WaitStrategies.incrementingWait(sleepTime, TimeUnit.SECONDS, 2, TimeUnit.SECONDS))
                // 重试监听器
                .withRetryListener(retryListener)
                .build();

        try {
            Object value = retryer.call(callable);
            if (value == null) {
                return null;
            } else {
                if (String.class.equals(resultClazz)) {
                    return (T) value;
                }
                return JacksonUtils.readValue(JacksonUtils.toJsonString(value), resultClazz);
            }
        } catch (Exception e) {
            log.error(">>>>> GuavaRetryingUtil retry error!", e);
        }
        return null;
    }

    /**
     * 自定义监听器
     */
    public static class MyRetryListener implements RetryListener {
        @Override
        public <V> void onRetry(Attempt<V> attempt) {
            log.info("监听器：监听次数={}", attempt.getAttemptNumber());
            if (attempt.hasException()) {
                log.error("监听器：监听异常信息", attempt.getExceptionCause());
            }
            if (attempt.hasResult()) {
                if (attempt.getResult() == null) {
                    log.info("监听器：返回结果为空！");
                } else {
                    log.info("监听器：返回结果数据：{}", attempt.getResult());
                }
            }
        }
    }

    /**
     * 模拟被调用的方法
     *
     * @return
     * @throws Exception
     */
    public static String invokeMethod() throws Exception {
        log.info("..........目标方法...........");
        // 模拟1：目标方法返回null时，是否触发重试机制
        //return null;

        // 模拟2：目标方法出现Exception时，是否触发重试机制
//        if(true){
//            throw new Exception("测试异常");
//        }
//        return null;

        // 模拟3：正常返回
        return "hello retry";
    }

    public static void say() {
        System.out.println("say hello");
    }
}
